package net.yegong.util.reflect;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;

import net.yegong.util.Pair;

/**
 * The LexicalCast is a type caster based on lexical content.
 * 
 * @author cooper
 */
public class LexicalCast {

	public static Object cast(String value, Type type) throws ReflectionException {
		if (type instanceof ParameterizedType) {
			return cast(value, (ParameterizedType) type);
		}
		if (type instanceof Class<?>) {
			return cast(value, (Class<?>) type);
		}
		throw new ReflectionException("Unknown type " + type.toString());
	}

	public static Object cast(String value, ParameterizedType type) throws ReflectionException {
		Class<?> clazz = (Class<?>) type.getRawType();
		Type[] typeArguments = type.getActualTypeArguments();
		if (Collection.class.isAssignableFrom(clazz)) {
			if (typeArguments.length == 1) {
				return castCollection(value, typeArguments[0]);
			}
		}
		if (Map.class.isAssignableFrom(clazz)) {
			if (typeArguments.length == 2) {
				return castMap(value, typeArguments);
			}
		}
		throw new ReflectionException("Unknown type " + type.toString());
	}

	public static Object castCollection(String value, Type type) throws ReflectionException {
		return cast(value, type);
	}

	public static Pair<Object, Object> castMap(String value, Type[] type) throws ReflectionException {
		int index = value.indexOf('=');
		if (index != -1 && index != value.length() - 1) {
			String first = value.substring(0, index);
			String second = value.substring(index + 1);
			return new Pair<Object, Object>(cast(first, type[0]), cast(second, type[1]));
		}
		throw new ReflectionException("Invalid value for a map entry " + value);
	}

	public static Object castArray(String value, Class<?> clazz) throws ReflectionException {
		Class<?> realClazz = clazz.getComponentType();
		String[] values = value.split(",");
		Object[] retVal = new Object[values.length];
		for (int i = 0; i < retVal.length; ++i) {
			retVal[i] = cast(values[i], realClazz);
		}
		return retVal;
	}

	public static Object cast(String value, Class<?> clazz) throws ReflectionException {
		clazz = TypeTraits.toWrapper(clazz);
		if (Boolean.class == clazz) {
			return Boolean.valueOf(value);
		}
		if (Character.class == clazz) {
			return value.charAt(0);
		}
		if (Byte.class == clazz) {
			return Byte.valueOf(value);
		}
		if (Short.class == clazz) {
			return Short.valueOf(value);
		}
		if (Integer.class == clazz) {
			return Integer.valueOf(value);
		}
		if (Long.class == clazz) {
			return Long.valueOf(value);
		}
		if (Float.class == clazz) {
			return Float.valueOf(value);
		}
		if (Double.class == clazz) {
			return Double.valueOf(value);
		}
		if (Void.class == clazz) {
			return null;
		}
		if (String.class == clazz) {
			return value;
		}
		if (Object.class == clazz) {
			return value;
		}
		if (clazz.isArray()) {
			return castArray(value, clazz);
		}
		throw new ReflectionException("Unknown type " + clazz.toString());
	}
}
